// Package cast 实现了一个C语言的抽象语法树
package cast

import (
	"fmt"
	"sort"
	"strconv"
	"strings"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/data"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

var Deferfunc func() = utils.Deferfunc

type UtoC struct {
	headerFile         *data.Slice[ast.CHeaderFile]
	PackageName        string
	FileName           string
	inAutoFreeFunc     []ast.Node
	inAutoFreeFuncInfo []ast.SymbolInfo
	varInit            []ast.ASSIGNInfo
	Nodes              []ast.Node
	genInstNodes       [][]ast.Node
	//自己导入的包
	importLocal []*Package
	isInitFunc  bool
	Thread      bool
	isDirIn     bool
	InAutoFree  bool
}

func NewUtoC() *UtoC {
	return &UtoC{}
}

func newUtoC(Thread bool, headerFiles *data.Slice[ast.CHeaderFile], InAutoFree bool) *UtoC {
	return &UtoC{Thread: Thread, headerFile: headerFiles, isDirIn: true, InAutoFree: InAutoFree}
}

func (c *UtoC) Parser(t *ast.Tree, inPackage bool) {
	c.parserInit(t)
	c.InAutoFree = t.IsHaveAutoFree.Load()
	//转换被导入包
	if !inPackage { //如果 Utoc 不在 Package 里
		c.headerFile = t.CHeaderFile
		c.genInstNodes = genericToC(c, t.GenInstNodes, c.Thread)
		sort.Slice(*t.ImportLoacl, func(i, j int) bool {
			return *(*t.ImportLoacl)[i].(*ast.Package).PackageName.Load() < *(*t.ImportLoacl)[j].(*ast.Package).PackageName.Load()
		})
		generateImport(&c.importLocal, *t.ImportLoacl, nil)
		//生成在自动释放块被调用的函数
		c.inAutoFreeFunc = generateInAutoFreeFunc(t.InAutoFreeFuncCallName, t.FindTree, c.findUtoc, t.Sbt, &c.inAutoFreeFuncInfo, t.PackageName)
		//建立按行升序的全局变量初始化顺序
		c.varInit = t.VarInitTable_File.InitOrder()
	}
}

func genericToC(c *UtoC, GenInstNodes *ast.Sbt, Thread bool) [][]ast.Node {
	type result struct {
		symbol string
		node   []ast.Node
	}
	var ret []result
	//TODO:改用更高效的数据结构
	GenInstNodes.Range(func(symbol string, info ast.SymbolInfo) bool {
		nodes := info.Info.(ast.RemoveGenericsInfo).Nodes
		decl, line := nodes[0].(ast.CDecl), retNodeLine(nodes[0])
		c.headerFile.Add(ast.CHeaderFile{N: decl, Line: line})
		ret = append(ret, result{symbol: symbol, node: nodes})
		return true
	})
	sort.Slice(ret, func(i, j int) bool {
		return ret[i].symbol < ret[j].symbol
	})
	var ret_ret = make([][]ast.Node, len(ret))
	for i, r := range ret {
		ret_ret[i] = r.node
	}
	return ret_ret
}

func (c *UtoC) parserInit(t *ast.Tree) {
	c.PackageName = t.PackageName
	c.FileName = t.Filename
	c.isInitFunc = t.IsInitFunc.Load()
	//转换所有节点为cast等价的，全局变量或常量放入HeaderFile字段中
	c.Nodes = t.Nodes
}

func (c *UtoC) String() string {
	var buf strings.Builder
	buf.Grow(10)
	buf.WriteString("&cast.Tree{\n")
	buf.WriteString("Nodes:")
	for i := 0; i < len(c.Nodes); i++ {
		buf.WriteString("\n")
		buf.WriteString(strconv.Itoa(i))
		buf.WriteString("\t")
		if c.Nodes[i] == nil {
			buf.WriteString("<nil>")
			continue
		}
		buf.WriteString(c.Nodes[i].String())
	}
	buf.WriteString("\nGenInst:")
	for i := 0; i < len(c.genInstNodes); i++ {
		buf.WriteString("\n")
		generateCNodeStr(c.genInstNodes[i], &buf)
	}
	buf.WriteString("}")
	return buf.String()
}

// findUtoc 寻找指定文件的 [UtoC] 指针
func (c *UtoC) findUtoc(path string) *UtoC {
	if c.FileName == path { //是不是寻找自己
		return c
	}
	for _, v := range c.importLocal { //在依赖中寻找
		if ptr := v.findUtoc(path, false); ptr != nil {
			return ptr
		}
	}
	panic(fmt.Errorf("未知的路径：%s", path))
}

func (c *UtoC) C() string {
	defer Deferfunc()
	var buf strings.Builder
	buf.Grow(10)
	sort.Slice(c.importLocal, func(i, j int) bool { //排序为了使输出可复现
		return c.importLocal[i].packageName < c.importLocal[j].packageName
	})
	for _, v := range c.importLocal { //依赖的包先生成
		v.C(&buf)
	}
	buf.WriteString(includeStr)
	if c.InAutoFree {
		buf.WriteString("#include \"mempool.h\"\n")
	}
	generateHeaderFile(c.headerFile.Data, c.inAutoFreeFuncInfo, &buf)
	tion := generateInit(c.PackageName, c.isInitFunc, c.varInit, &buf, c.importLocal, false)
	generateCFile(c.Nodes, &buf)
	for _, v := range c.genInstNodes { //生成泛型实例化
		generateCFile(v, &buf)
	}
	generateCFile(c.inAutoFreeFunc, &buf)
	buf.WriteString("\n")
	buf.WriteString(tion)
	return buf.String()
}

const includeStr = `#include <stdbool.h> 
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

`
